SREP-1107: Added a new 'managed-scripts' subcommand to osdctl promote#855
Conversation
|
@Nikokolas3270: This pull request references SREP-1107 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
Important Review skippedAuto reviews are limited based on label configuration. 🚫 Review skipped — only excluded labels are configured. (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughReplaces legacy app-interface git/SAAS discovery with a registry-driven promotion model: adds go-git repo utilities, AppInterfaceClone, Service/Application models, a ServicesRegistry, new managedscripts subcommand and tests; removes PKO, pathutil, git/app_interface/service_repo implementations, and many legacy saas helpers and tests. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@Nikokolas3270: This pull request references SREP-1107 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
There was a problem hiding this comment.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
cmd/promote/dynatrace/dt_utils.go (2)
175-178:⚠️ Potential issue | 🟠 MajorReturn immediately when opening the file fails.
The current flow logs the error and continues, which can pass an invalid file handle into downstream processing.
Proposed fix
file, err := Open(filename) if err != nil { - fmt.Println(err) + return fmt.Errorf("failed to open file '%s': %w", filename, err) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dt_utils.go` around lines 175 - 178, The Open(filename) call currently prints the error and continues, which can pass an invalid file handle into downstream code; update the error handling at the Open(...) site so that on err you return the error (or otherwise abort the function) instead of just fmt.Println(err). Locate the Open(filename) invocation in dt_utils.go and modify the surrounding function to propagate the error (or call return) immediately when err != nil, ensuring downstream code does not receive a nil/invalid file handle.
142-153:⚠️ Potential issue | 🟠 MajorPropagate
os.ReadDirfailures instead of discarding them.Read errors are currently ignored at multiple levels, so the promotion can incorrectly continue as if files were scanned successfully.
Proposed fix
- items, _ := os.ReadDir(dir) + items, err := os.ReadDir(dir) + if err != nil { + return "", fmt.Errorf("failed to read directory '%s': %w", dir, err) + } ... - subitems, _ := os.ReadDir(subDir) + subitems, err := os.ReadDir(subDir) + if err != nil { + return "", fmt.Errorf("failed to read directory '%s': %w", subDir, err) + } ... - subitems2, _ := os.ReadDir(subDir2) + subitems2, err := os.ReadDir(subDir2) + if err != nil { + return "", fmt.Errorf("failed to read directory '%s': %w", subDir2, err) + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dt_utils.go` around lines 142 - 153, The os.ReadDir calls in the nested directory walk (the reads that populate items, subitems and subitems2) currently ignore their returned errors; change each call to capture the error (e.g., items, err := os.ReadDir(dir)) and propagate it instead of discarding it—return the error (or wrap and return) from the enclosing function (the directory-walking function in dt_utils.go) so failures stop the promotion; do the same for subitems and subitems2 reads and adjust calling code to handle the returned error.cmd/promote/dynatrace/dynatrace.go (1)
73-74:⚠️ Potential issue | 🟠 MajorDon’t ignore list errors before successful exit.
Both list branches discard the returned error and then exit with success, which can hide real failures from users and automation.
Proposed fix
- _ = listDynatraceModuleNames(dynatraceConfig) - os.Exit(0) + if err := listDynatraceModuleNames(dynatraceConfig); err != nil { + return fmt.Errorf("failed to list dynatrace modules: %w", err) + } + return nil ... - _ = listServiceIds(servicesRegistry) - os.Exit(0) + if err := listServiceIds(servicesRegistry); err != nil { + return fmt.Errorf("failed to list dynatrace components: %w", err) + } + return nilAlso applies to: 117-118
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dynatrace.go` around lines 73 - 74, The calls to listDynatraceModuleNames currently discard the returned error and then call os.Exit(0); change both call sites to capture the error (e.g., err := listDynatraceModuleNames(dynatraceConfig)), check if err != nil, print or log the error to stderr (or use the existing logger) and call os.Exit(1); if no error, then exit with success. This ensures failures from listDynatraceModuleNames are surfaced instead of being silently ignored.
🧹 Nitpick comments (5)
cmd/promote/dynatrace/dt_utils.go (1)
108-124: Reset module caches inGetModulesNamesbefore repopulating.
ModulesSliceandModulesFilesMapare package globals and currently accumulate across repeated calls.Proposed fix
func GetModulesNames(baseDir, dir string) ([]string, error) { + ModulesSlice = ModulesSlice[:0] + ModulesFilesMap = map[string]string{} + dirGlob := filepath.Join(baseDir, dir) filepaths, err := os.ReadDir(dirGlob)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dt_utils.go` around lines 108 - 124, GetModulesNames currently appends to package globals ModulesSlice and ModulesFilesMap on each call; modify GetModulesNames to reset/clear ModulesSlice and reinitialize or clear ModulesFilesMap at the start of the function (before reading dir entries) so repeated calls don't accumulate stale data; locate the function GetModulesNames and add logic to set ModulesSlice = nil (or empty slice) and ModulesFilesMap = make(map[string]string) (or clear existing entries) before populating them.cmd/promote/utils/service.go (4)
42-44: Inconsistent receiver name onyamlDoc.
GetFilePath(line 38) andSave(line 46) used, butGetNameusess. Use a consistent receiver name across all methods of the same type.Proposed fix
-func (s *yamlDoc) GetName() string { - return s.name +func (d *yamlDoc) GetName() string { + return d.name }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/utils/service.go` around lines 42 - 44, The GetName method for type yamlDoc uses an inconsistent receiver name `s`; change its receiver to `d` to match the other methods on yamlDoc (e.g., GetFilePath and Save) so all methods use the same receiver identifier; update the method signature for GetName to use `d` and keep the body returning d.name.
413-419: Nit: unconventional blank line in error handling.The blank line between the call on line 414 and the
if err != nilon line 416 breaks the standard Go error-handling idiom. Consider removing it for consistency.Proposed fix
if newHash == "" { newHash, err = repo.GetHeadHash() - if err != nil { return err } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/utils/service.go` around lines 413 - 419, Remove the extra blank line in the error handling after calling repo.GetHeadHash so the idiomatic Go pattern is preserved; specifically, in the block that assigns newHash (the call to repo.GetHeadHash) and the subsequent if err != nil block, bring the if directly under the call (no blank line) to keep the standard newHash, err := repo.GetHeadHash / if err != nil style used elsewhere.
296-330: Partial failure leaves committed state in the local branch.Each
resourceTemplatePromotion.promotecall saves the YAML and commits independently (lines 307, 318). If a later promotion in the loop (line 428) fails, earlier promotions are already committed to the local branch. This is likely acceptable since the user must manually push, but consider documenting this behavior or logging a warning when a partial failure occurs so users know what's been committed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/utils/service.go` around lines 296 - 330, The promote function (resourceTemplatePromotion.promote) commits each resource independently (uses callbacks.SetTargetHash, service.Save, service.appInterfaceClone.Commit) which can leave the local branch partially committed if a later promotion fails; update the caller that loops over promotions (the code that invokes resourceTemplatePromotion.promote) to track which promotions succeeded and, on any subsequent error, emit a clear warning/log that lists the committed resource relPaths and informs the user that the local branch contains partial commits requiring manual push/revert; alternatively expose a boolean or error type from promote indicating "committed" so the caller can build that list before logging the warning.
21-21: Export theyamlDoctype to match its exported function.
ReadYamlDocFromFileis exported and used externally (e.g.,cmd/promote/saas/saas.go:118), but it returns*yamlDoc, which is unexported. This violates Go conventions — external callers cannot declare variables of this type. ExportyamlDocasYamlDoc(or use the lowercase-exported pattern if intentional, but document it).Additionally, the receiver names for
yamlDocmethods are inconsistent:GetFilePath()andSave()use receiverd, whileGetName()(line 42) uses receivers. Standardize on a single receiver name liked.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/utils/service.go` at line 21, The exported function ReadYamlDocFromFile returns an unexported type yamlDoc which prevents external callers from using its value; rename the type to exported YamlDoc (or change the function to return an exported interface) and update all references accordingly (including ReadYamlDocFromFile's return type and any uses in cmd/promote/saas/saas.go). Also standardize method receiver names on that type: change the GetName receiver from 's' to 'd' to match GetFilePath and Save so all methods on yamlDoc/YamlDoc use a consistent receiver (d). Ensure you update any imports/usages and tests to the new exported type name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmd/promote/managedscripts/managed_scripts.go`:
- Around line 30-32: The FilterTargets method currently hardcodes
prodNamespaceRef when calling utils.FilterTargetsContainingNamespaceRef, so the
configured --namespaceRef value is ignored; update
promoteCallbacks.FilterTargets to pass the instance/configured namespace
reference (e.g., c.namespaceRef or the field that holds the CLI flag) into
utils.FilterTargetsContainingNamespaceRef instead of prodNamespaceRef, ensuring
the same change is applied where else prodNamespaceRef is used for target
selection so the runtime flag actually influences filtering.
In `@cmd/promote/saas/saas.go`:
- Line 234: Update the CLI help/example string to use the canonical flag
--serviceId instead of the legacy alias --serviceName: locate the usage text
that currently reads `osdctl promote saas --serviceName <service> --gitHash
<git-hash>` (in the promote command's help/usage output) and replace
`--serviceName` with `--serviceId`; ensure any adjacent help text or examples
referencing the legacy alias are also updated so the example consistently shows
`--serviceId <service>` with `--gitHash <git-hash>`.
- Around line 271-274: Add a fail-fast required check for ops.serviceId before
calling servicesRegistry.GetService to give a clearer error for promote mode:
validate that ops.serviceId is non-empty (e.g., at the start of the promote
handler or before the GetService call), and return a descriptive error if it's
empty (mentioning that --serviceId is required in promote mode). Update the code
path where servicesRegistry.GetService(ops.serviceId) is invoked (reference
ops.serviceId and the promote handler function) to perform this explicit check
and only call GetService when the value is present.
- Around line 38-43: The current logic returns the original filePath when
deploy.yaml is missing, which can register a directory as a service; change the
code to only return valid regular file paths: after checking subFilePath, also
verify that filePath itself is a regular file (use os.Stat(filePath) and
fileInfo.Mode().IsRegular()) before returning it, and if it's a directory or not
a regular file return an empty string (or an error sentinel) so the caller can
skip this SaaS entry; adjust callers of this helper to treat empty string as
"skip invalid entry".
In `@cmd/promote/utils/git_repo.go`:
- Around line 54-89: The code pre-marks commonAncestorCommit.Hash in
visitedHashes before traversal which allows walks that never actually reach
commonAncestorHash to succeed silently; modify the traversal in the function
containing queue, visitedHashes, commit.IsAncestor and the final sb.WriteString
branch so that you either do not pre-populate visitedHashes with
commonAncestorCommit.Hash or (preferably) add a post-traversal check that
verifies visitedHashes contains commonAncestorHash (or that you visited it
during traversal) and return an explicit error if it was not reached; use the
symbols visitedHashes, commonAncestorCommit / commonAncestorHash, queue,
commit.IsAncestor and the final return path to locate where to add the check.
In `@cmd/promote/utils/service.go`:
- Around line 212-229: The function FilterTargetsContainingNamespaceRef
currently uses strings.Contains(visitedNamespaceRef, namespaceRef) which allows
substring matches; change the check to a precise match (e.g., verify the path
ends with the expected segment or exact identifier) by replacing the contains
logic with a more strict comparison such as using strings.HasSuffix or parsing
visitedNamespaceRef into path segments and comparing the final segment to
namespaceRef (operate on visitedNamespaceRef and namespaceRef variables inside
FilterTargetsContainingNamespaceRef so only exact/segment matches are
considered).
- Around line 141-146: The code reads appRelFilePath from
yamlDoc.rootNode.GetString and directly uses
filepath.Join(appInterfaceClone.GetPath(), "data", appRelFilePath) before
calling readApplicationFromFile; sanitize appRelFilePath by running
filepath.Clean, reject absolute paths or any path that resolves outside the
intended base (e.g. contains ".." segments that escape the
appInterfaceClone.GetPath()/data directory), and construct the final path by
joining the cleaned relative path to appInterfaceClone.GetPath()/data then
verifying the resulting path has the base directory as a prefix before calling
readApplicationFromFile.
- Around line 389-394: Iterate deterministically over oldHashToTargetNodes by
collecting its keys into a slice, sorting that slice (using sort.Strings), and
then building resourceTemplatePromotions in the sorted order (instead of ranging
the map directly); update the import block to include "sort". Ensure the same
symbols are used: oldHashToTargetNodes, resourceTemplatePromotions,
resourceTemplatePromotion, and that downstream code that calls promote will now
see a stable commit order.
In `@cmd/promote/utils/services_registry.go`:
- Around line 19-33: The constructor NewServicesRegistry should guard against
nil inputs to avoid panics: at the start of the function check if
appInterfaceClone is nil and if so return a descriptive error (do not call
appInterfaceClone.GetPath()); also check if validateServiceFilePathCallback is
nil and return a descriptive error before invoking it later; update
callers/tests if necessary to pass non-nil values or to handle the returned
error from NewServicesRegistry.
- Around line 36-37: The code silently overwrites existing entries in the
serviceIdToFilePath map (variable serviceId) when scanning files; change the
insertion logic to fail fast on duplicates by checking if serviceId already
exists in serviceIdToFilePath and returning/propagating an error (or logging and
exiting) that includes the conflicting serviceId and both file paths (existing
value and serviceFilePath) so callers of the scanning function can handle the
failure instead of allowing silent overwrite.
In `@docs/osdctl_promote_managedscripts.md`:
- Around line 5-39: Add language identifiers to all fenced code blocks in
docs/osdctl_promote_managedscripts.md to satisfy MD040: mark the command usage
block as "text" (or "bash" if preferred), the example block as "bash", and both
options blocks (Options and Options inherited from parent commands) as "text".
Update the fences surrounding the snippets that contain "osdctl promote
managedscripts [flags]", the example starting with "# Promote managed-scripts
repo", the options list beginning with "--appInterfaceDir", and the inherited
options list beginning with "--as" to include the appropriate language tags.
In `@docs/osdctl_promote_saas.md`:
- Around line 22-30: The fenced code block showing CLI flags in the saas
promotion docs lacks a language identifier which triggers markdownlint MD040;
update the triple-backtick opening fence to include a language (e.g., ```text)
for the block containing the lines starting with "--appInterfaceDir" and ending
with "--serviceId" so the block is treated as plain text and the linter warning
is resolved.
- Line 17: The example for the CLI invocation uses the deprecated alias flag
--serviceName; update the example for the command osdctl promote saas to use the
canonical flag --serviceId instead (replace the --serviceName token with
--serviceId in the example line) so the documentation shows the current,
supported flag.
In `@docs/README.md`:
- Around line 3938-3940: Add a language identifier to the fenced code block that
contains the line "osdctl promote managedscripts [flags]" so markdownlint MD040
is satisfied; modify the opening fence from ``` to ```text (or ```bash) around
the block in README.md so it becomes ```text followed by the command and closing
``` to preserve formatting.
---
Outside diff comments:
In `@cmd/promote/dynatrace/dt_utils.go`:
- Around line 175-178: The Open(filename) call currently prints the error and
continues, which can pass an invalid file handle into downstream code; update
the error handling at the Open(...) site so that on err you return the error (or
otherwise abort the function) instead of just fmt.Println(err). Locate the
Open(filename) invocation in dt_utils.go and modify the surrounding function to
propagate the error (or call return) immediately when err != nil, ensuring
downstream code does not receive a nil/invalid file handle.
- Around line 142-153: The os.ReadDir calls in the nested directory walk (the
reads that populate items, subitems and subitems2) currently ignore their
returned errors; change each call to capture the error (e.g., items, err :=
os.ReadDir(dir)) and propagate it instead of discarding it—return the error (or
wrap and return) from the enclosing function (the directory-walking function in
dt_utils.go) so failures stop the promotion; do the same for subitems and
subitems2 reads and adjust calling code to handle the returned error.
In `@cmd/promote/dynatrace/dynatrace.go`:
- Around line 73-74: The calls to listDynatraceModuleNames currently discard the
returned error and then call os.Exit(0); change both call sites to capture the
error (e.g., err := listDynatraceModuleNames(dynatraceConfig)), check if err !=
nil, print or log the error to stderr (or use the existing logger) and call
os.Exit(1); if no error, then exit with success. This ensures failures from
listDynatraceModuleNames are surfaced instead of being silently ignored.
---
Nitpick comments:
In `@cmd/promote/dynatrace/dt_utils.go`:
- Around line 108-124: GetModulesNames currently appends to package globals
ModulesSlice and ModulesFilesMap on each call; modify GetModulesNames to
reset/clear ModulesSlice and reinitialize or clear ModulesFilesMap at the start
of the function (before reading dir entries) so repeated calls don't accumulate
stale data; locate the function GetModulesNames and add logic to set
ModulesSlice = nil (or empty slice) and ModulesFilesMap =
make(map[string]string) (or clear existing entries) before populating them.
In `@cmd/promote/utils/service.go`:
- Around line 42-44: The GetName method for type yamlDoc uses an inconsistent
receiver name `s`; change its receiver to `d` to match the other methods on
yamlDoc (e.g., GetFilePath and Save) so all methods use the same receiver
identifier; update the method signature for GetName to use `d` and keep the body
returning d.name.
- Around line 413-419: Remove the extra blank line in the error handling after
calling repo.GetHeadHash so the idiomatic Go pattern is preserved; specifically,
in the block that assigns newHash (the call to repo.GetHeadHash) and the
subsequent if err != nil block, bring the if directly under the call (no blank
line) to keep the standard newHash, err := repo.GetHeadHash / if err != nil
style used elsewhere.
- Around line 296-330: The promote function (resourceTemplatePromotion.promote)
commits each resource independently (uses callbacks.SetTargetHash, service.Save,
service.appInterfaceClone.Commit) which can leave the local branch partially
committed if a later promotion fails; update the caller that loops over
promotions (the code that invokes resourceTemplatePromotion.promote) to track
which promotions succeeded and, on any subsequent error, emit a clear
warning/log that lists the committed resource relPaths and informs the user that
the local branch contains partial commits requiring manual push/revert;
alternatively expose a boolean or error type from promote indicating "committed"
so the caller can build that list before logging the warning.
- Line 21: The exported function ReadYamlDocFromFile returns an unexported type
yamlDoc which prevents external callers from using its value; rename the type to
exported YamlDoc (or change the function to return an exported interface) and
update all references accordingly (including ReadYamlDocFromFile's return type
and any uses in cmd/promote/saas/saas.go). Also standardize method receiver
names on that type: change the GetName receiver from 's' to 'd' to match
GetFilePath and Save so all methods on yamlDoc/YamlDoc use a consistent receiver
(d). Ensure you update any imports/usages and tests to the new exported type
name.
ℹ️ Review info
Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (23)
cmd/promote/cmd.gocmd/promote/dynatrace/dt_utils.gocmd/promote/dynatrace/dynatrace.gocmd/promote/dynatrace/utils_test.gocmd/promote/git/app_interface.gocmd/promote/git/app_interface_test.gocmd/promote/git/service_repo.gocmd/promote/git/service_repo_test.gocmd/promote/managedscripts/managed_scripts.gocmd/promote/pathutil/pathutil_test.gocmd/promote/pko/pko.gocmd/promote/saas/saas.gocmd/promote/saas/utils.gocmd/promote/saas/utils_test.gocmd/promote/utils/app_interface_clone.gocmd/promote/utils/git_repo.gocmd/promote/utils/service.gocmd/promote/utils/services_registry.godocs/README.mddocs/osdctl_promote.mddocs/osdctl_promote_managedscripts.mddocs/osdctl_promote_saas.mdgo.mod
💤 Files with no reviewable changes (8)
- cmd/promote/pathutil/pathutil_test.go
- cmd/promote/pko/pko.go
- cmd/promote/saas/utils.go
- cmd/promote/git/app_interface_test.go
- cmd/promote/git/service_repo.go
- cmd/promote/git/app_interface.go
- cmd/promote/git/service_repo_test.go
- cmd/promote/saas/utils_test.go
There was a problem hiding this comment.
The promotion command's code finally looks much better 😄 Added a few comments, aside from that;
-
There was decent test coverage for the command before which is now gone; would be nice to have it back but can also be done as a follow-up, we do already have a card for this in backlog: https://issues.redhat.com/browse/SREP-1278
-
it looks like pathutil/pathutil.go isn't being used anywhere now, so we could remove that orphaned code with this PR too
I tried to go through the coderabbit suggestions as well and commented/reacted to them based on whether or not they make sense 😄
Thanks :)
True, I am still working on the unit-tests, no worries. I just wanted to have people a first look on that finally quite big change.
Thanks! I will TAL as well |
90a34b6 to
b040d1e
Compare
|
@Nikokolas3270: This pull request references SREP-1107 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
cmd/promote/dynatrace/dt_utils.go (1)
137-166:⚠️ Potential issue | 🟡 MinorErrors from
os.ReadDirare silently ignored.Lines 140 and 145 discard errors from
os.ReadDir. This could mask permission issues or missing directories, leading to confusing behavior.🛡️ Proposed fix
func updatePromotionGitHash(module string, dir string, promotionGitHash string) (string, error) { fmt.Printf("Iterating over directory : %s", dir) - items, _ := os.ReadDir(dir) + items, err := os.ReadDir(dir) + if err != nil { + return "", fmt.Errorf("failed to read directory '%s': %v", dir, err) + } for _, item := range items { fmt.Println("Production tenant: ", item.Name()) if item.IsDir() { subDir := filepath.Join(dir, item.Name()) - subitems, _ := os.ReadDir(subDir) + subitems, err := os.ReadDir(subDir) + if err != nil { + return "", fmt.Errorf("failed to read subdirectory '%s': %v", subDir, err) + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dt_utils.go` around lines 137 - 166, The function updatePromotionGitHash currently ignores errors returned by os.ReadDir (when listing dir, subDir, and subDir2), which can hide permission or missing-directory problems; update each os.ReadDir call to capture its error, check it, and return a wrapped error (including the path and original error) instead of proceeding silently so callers can fail fast—apply this to the top-level items, subitems and subitems2 reads in updatePromotionGitHash and propagate or return errors from updateFileContent as appropriate.
♻️ Duplicate comments (1)
cmd/promote/dynatrace/dynatrace.go (1)
126-131:⚠️ Potential issue | 🟠 MajorReturn the promotion error instead of exiting the process.
os.Exit(1)bypasses Cobra'sRunEerror path and skips deferred cleanup/tests.🔧 Proposed fix
- err = service.Promote(&utils.DefaultPromoteCallbacks{Service: service}, ops.gitHash) - - if err != nil { - fmt.Printf("Error while promoting service: %v\n", err) - os.Exit(1) - } + if err := service.Promote(&utils.DefaultPromoteCallbacks{Service: service}, ops.gitHash); err != nil { + return fmt.Errorf("error while promoting service: %w", err) + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dynatrace.go` around lines 126 - 131, The code currently calls fmt.Printf and os.Exit(1) after service.Promote fails, which prevents Cobra's RunE error handling and deferred cleanup; change the block in the function that calls service.Promote(&utils.DefaultPromoteCallbacks{Service: service}, ops.gitHash) to return the error (or a wrapped error with context) instead of printing and exiting so the caller/RunE can handle it and deferred cleanup runs; specifically remove fmt.Printf/os.Exit and return err (or fmt.Errorf("promote failed: %w", err)) from the surrounding command handler.
🧹 Nitpick comments (3)
cmd/promote/utils/git_repo.go (1)
70-75: Minor: Missing space in warning message.Line 73 has
"Warning:Failed"- should be"Warning: Failed"for consistency with line 40.✏️ Fix formatting
func (r *Repo) Cleanup() { err := os.RemoveAll(r.clonePath) if err != nil { - fmt.Printf("Warning:Failed to remove clone directory '%s': %v", r.clonePath, err) + fmt.Printf("Warning: Failed to remove clone directory '%s': %v", r.clonePath, err) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/utils/git_repo.go` around lines 70 - 75, The warning string in Repo.Cleanup uses "Warning:Failed" without a space; update the fmt.Printf call inside Cleanup (function Repo.Cleanup) to insert a space after the colon (i.e., "Warning: Failed to remove clone directory '%s': %v") so the message matches the formatting used elsewhere (referencing r.clonePath and the fmt.Printf call).cmd/promote/dynatrace/dt_utils.go (1)
25-28: Package-level mutable state can cause test pollution.
ModulesSliceandModulesFilesMapare package-level variables thatGetModulesNamesappends to. This can cause issues if tests run in parallel or if the function is called multiple times.Consider returning fresh slices/maps instead of mutating package-level state, or clearing them at the start of
GetModulesNames.Also applies to: 106-123
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dt_utils.go` around lines 25 - 28, ModulesSlice and ModulesFilesMap are package-level mutable variables that GetModulesNames appends to, causing test pollution; change GetModulesNames to use and return fresh local variables (e.g., local modulesSlice []string and modulesFilesMap map[string]string) instead of appending to the package-level ModulesSlice/ModulesFilesMap, and update callers to accept the returned slice/map, or alternatively clear ModulesSlice and ModulesFilesMap at the start of GetModulesNames before use; apply the same local/clear fix to the other similar code block referenced around lines 106-123 (same function or helper) so no package-level mutation persists across calls/tests.cmd/promote/utils/app_interface_clone.go (1)
110-128: Consider detecting the default branch dynamically instead of hardcodingmaster.The function assumes
masteris the default branch, which is correct for osdctl. However, many repositories now usemainas the default. For robustness, consider detecting the default branch dynamically or making it configurable, so the function doesn't fail if used with repositories that have adoptedmain.♻️ Suggested approach
-func (a *AppInterfaceClone) CheckoutNewBranch(branchName string) error { - if err := a.workTree.Checkout(&git.CheckoutOptions{Branch: plumbing.NewBranchReferenceName("master")}); err != nil { - return fmt.Errorf("failed to checkout master branch in '%s': %v", a.path, err) +func (a *AppInterfaceClone) CheckoutNewBranch(branchName string, baseBranch string) error { + if baseBranch == "" { + baseBranch = "master" + } + if err := a.workTree.Checkout(&git.CheckoutOptions{Branch: plumbing.NewBranchReferenceName(baseBranch)}); err != nil { + return fmt.Errorf("failed to checkout %s branch in '%s': %v", baseBranch, a.path, err) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/utils/app_interface_clone.go` around lines 110 - 128, CheckoutNewBranch currently hardcodes "master"; change it to detect the repository's current/default branch instead and use that for the initial checkout. In CheckoutNewBranch use repo.Head() (or resolve "refs/remotes/origin/HEAD" if you need remote default) to obtain the default branch reference name and pass that into workTree.Checkout instead of plumbing.NewBranchReferenceName("master"), falling back to "master" or "main" only if detection fails; keep existing logic for branchReference creation, repo.Reference check, RemoveReference and final Checkout(Create:true).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmd/promote/dynatrace/dynatrace.go`:
- Around line 93-117: Move the checks that validate non-terraform flags (the
ops.list, ops.component and ops.gitHash logic and the error returns like "--list
cannot be used with --component or --gitHash" and "--component is required
unless --list is used") to run before calling utils.FindAppInterfaceClone and
utils.NewServicesRegistry so missing/invalid flags are caught immediately; i.e.,
validate ops.list/ops.component/ops.gitHash at the top of the function and
return the appropriate errors (or call listServiceIds when ops.list) before
invoking FindAppInterfaceClone or NewServicesRegistry.
In `@cmd/promote/managedscripts/managed_scripts.go`:
- Around line 64-70: The command registered in the Cobra command definition uses
Use: "managedscripts" which does not match the advertised "managed-scripts";
update the Cobra command in managed_scripts.go to either change the Use value to
"managed-scripts" or add an Alias entry including "managedscripts" (or both) so
both forms work; locate the command struct where Use, Short, Args,
DisableAutoGenTag and Example are set and modify the Use/Aliases fields
accordingly to ensure "osdctl promote managed-scripts" is recognized.
In `@cmd/promote/utils/services_registry_test.go`:
- Around line 39-59: The test asserts that GetServicesIds() advertises
"service-4" but GetService("service-4") fails, creating a contract mismatch;
either make GetService succeed for advertised services or stop advertising it.
Fix by updating the test fixture or registry setup used by
services_registry_test.go so the declared service IDs from GetServicesIds()
match retrievable services from GetService() — e.g., ensure the underlying
registry data includes a valid entry/file for "service-4" (so GetService returns
a non-nil service and no error) or remove "service-4" from the expected slice in
the GetServicesIds() assertion; keep references to GetServicesIds and GetService
when applying the change.
---
Outside diff comments:
In `@cmd/promote/dynatrace/dt_utils.go`:
- Around line 137-166: The function updatePromotionGitHash currently ignores
errors returned by os.ReadDir (when listing dir, subDir, and subDir2), which can
hide permission or missing-directory problems; update each os.ReadDir call to
capture its error, check it, and return a wrapped error (including the path and
original error) instead of proceeding silently so callers can fail fast—apply
this to the top-level items, subitems and subitems2 reads in
updatePromotionGitHash and propagate or return errors from updateFileContent as
appropriate.
---
Duplicate comments:
In `@cmd/promote/dynatrace/dynatrace.go`:
- Around line 126-131: The code currently calls fmt.Printf and os.Exit(1) after
service.Promote fails, which prevents Cobra's RunE error handling and deferred
cleanup; change the block in the function that calls
service.Promote(&utils.DefaultPromoteCallbacks{Service: service}, ops.gitHash)
to return the error (or a wrapped error with context) instead of printing and
exiting so the caller/RunE can handle it and deferred cleanup runs; specifically
remove fmt.Printf/os.Exit and return err (or fmt.Errorf("promote failed: %w",
err)) from the surrounding command handler.
---
Nitpick comments:
In `@cmd/promote/dynatrace/dt_utils.go`:
- Around line 25-28: ModulesSlice and ModulesFilesMap are package-level mutable
variables that GetModulesNames appends to, causing test pollution; change
GetModulesNames to use and return fresh local variables (e.g., local
modulesSlice []string and modulesFilesMap map[string]string) instead of
appending to the package-level ModulesSlice/ModulesFilesMap, and update callers
to accept the returned slice/map, or alternatively clear ModulesSlice and
ModulesFilesMap at the start of GetModulesNames before use; apply the same
local/clear fix to the other similar code block referenced around lines 106-123
(same function or helper) so no package-level mutation persists across
calls/tests.
In `@cmd/promote/utils/app_interface_clone.go`:
- Around line 110-128: CheckoutNewBranch currently hardcodes "master"; change it
to detect the repository's current/default branch instead and use that for the
initial checkout. In CheckoutNewBranch use repo.Head() (or resolve
"refs/remotes/origin/HEAD" if you need remote default) to obtain the default
branch reference name and pass that into workTree.Checkout instead of
plumbing.NewBranchReferenceName("master"), falling back to "master" or "main"
only if detection fails; keep existing logic for branchReference creation,
repo.Reference check, RemoveReference and final Checkout(Create:true).
In `@cmd/promote/utils/git_repo.go`:
- Around line 70-75: The warning string in Repo.Cleanup uses "Warning:Failed"
without a space; update the fmt.Printf call inside Cleanup (function
Repo.Cleanup) to insert a space after the colon (i.e., "Warning: Failed to
remove clone directory '%s': %v") so the message matches the formatting used
elsewhere (referencing r.clonePath and the fmt.Printf call).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 293682b2-48bb-4f01-8afe-c1686d9ba4c5
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (31)
cmd/promote/cmd.gocmd/promote/dynatrace/dt_utils.gocmd/promote/dynatrace/dynatrace.gocmd/promote/dynatrace/utils_test.gocmd/promote/git/app_interface.gocmd/promote/git/app_interface_test.gocmd/promote/git/service_repo.gocmd/promote/git/service_repo_test.gocmd/promote/managedscripts/managed_scripts.gocmd/promote/managedscripts/managed_scripts_test.gocmd/promote/pathutil/pathutil.gocmd/promote/pathutil/pathutil_test.gocmd/promote/pko/pko.gocmd/promote/saas/saas.gocmd/promote/saas/saas_test.gocmd/promote/saas/utils.gocmd/promote/saas/utils_test.gocmd/promote/utils/app_interface_clone.gocmd/promote/utils/app_interface_clone_test.gocmd/promote/utils/git_repo.gocmd/promote/utils/service.gocmd/promote/utils/service_test.gocmd/promote/utils/services_registry.gocmd/promote/utils/services_registry_test.gocmd/promote/utils/test_tools.gocmd/promote/utils/utils_test.godocs/README.mddocs/osdctl_promote.mddocs/osdctl_promote_managedscripts.mddocs/osdctl_promote_saas.mdgo.mod
💤 Files with no reviewable changes (9)
- cmd/promote/git/service_repo.go
- cmd/promote/pathutil/pathutil_test.go
- cmd/promote/pathutil/pathutil.go
- cmd/promote/git/service_repo_test.go
- cmd/promote/saas/utils_test.go
- cmd/promote/git/app_interface_test.go
- cmd/promote/saas/utils.go
- cmd/promote/pko/pko.go
- cmd/promote/git/app_interface.go
✅ Files skipped from review due to trivial changes (3)
- docs/osdctl_promote_managedscripts.md
- docs/osdctl_promote.md
- go.mod
🚧 Files skipped from review as they are similar to previous changes (5)
- cmd/promote/cmd.go
- cmd/promote/dynatrace/utils_test.go
- cmd/promote/utils/services_registry.go
- docs/osdctl_promote_saas.md
- docs/README.md
b040d1e to
af5846c
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (4)
cmd/promote/dynatrace/dynatrace.go (2)
93-117:⚠️ Potential issue | 🟠 MajorValidate flags before opening the app-interface clone.
Line 93 and Line 98 do repo discovery before the
--list/--componentchecks. As a result,--liststill goes through the old usage path, and missing-flag invocations can fail on clone/registry setup instead of returning the real CLI error. Move the non-terraform flag validation ahead ofFindAppInterfaceClone/NewServicesRegistry.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dynatrace.go` around lines 93 - 117, The CLI flag validation for non-terraform operations should run before cloning the repo and constructing the services registry: move the checks that validate ops.list, ops.component, and ops.gitHash (the logic that returns errors for "--list cannot be used with --component or --gitHash" and "--component is required unless --list is used") above the calls to utils.FindAppInterfaceClone and utils.NewServicesRegistry; if ops.list is true preserve setting cmd.SilenceUsage = true and directly call listServiceIds(servicesRegistry) only after constructing the registry when actually needed, but prevent any repo clone/registry setup when basic flag validation would already return an error. Ensure the condition ordering around ops.list, ops.component and ops.gitHash is performed first so invalid flag combinations short-circuit before invoking FindAppInterfaceClone or NewServicesRegistry.
128-130:⚠️ Potential issue | 🟠 MajorReturn the
RunEerror instead of exiting the process.This branch still bypasses Cobra's error handling and makes the command harder to test.
Proposed fix
- if err != nil { - fmt.Printf("Error while promoting service: %v\n", err) - os.Exit(1) - } + if err != nil { + return fmt.Errorf("error while promoting service: %w", err) + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dynatrace.go` around lines 128 - 130, The code inside the Cobra command's RunE handler (the RunE closure in dynatrace.go that currently prints the error and calls os.Exit(1)) should not exit the process; instead remove the fmt.Printf and os.Exit(1) and return the error to let Cobra handle it. Update the error branch in the RunE closure (the block that checks if err != nil after promoting the service) to return a wrapped or raw err (e.g., fmt.Errorf("promote service: %w", err) or simply return err) so tests and Cobra's error handling can observe the failure.cmd/promote/utils/services_registry.go (1)
35-37:⚠️ Potential issue | 🟠 MajorReject duplicate service IDs instead of overwriting them.
Because
serviceIdis just the basename, Line 37 silently replaces an earlier entry if two scanned directories contain the same filename. Since this constructor accepts multiple directories, that can point a promotion at the wrong service file.Proposed fix
if serviceFilePath != "" { serviceId := strings.TrimSuffix(fileName, filepath.Ext(fileName)) + if existingPath, exists := serviceIdToFilePath[serviceId]; exists && existingPath != serviceFilePath { + return nil, fmt.Errorf("duplicate service id %q mapped to both %q and %q", serviceId, existingPath, serviceFilePath) + } serviceIdToFilePath[serviceId] = serviceFilePath }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/utils/services_registry.go` around lines 35 - 37, Change the code that assigns serviceIdToFilePath[serviceId] = serviceFilePath to detect duplicates and reject them instead of overwriting: before inserting, check if serviceId already exists in serviceIdToFilePath; if it does, return an error (or fail construction) that includes the conflicting serviceId and both file paths (existing and new) so the caller can detect which scanned directories produced the collision. Update the constructor/loader function that iterates fileName/serviceFilePath to perform this check and propagate the error back to callers.cmd/promote/saas/saas.go (1)
34-45:⚠️ Potential issue | 🟡 MinorDon't return a directory as a service file.
Line 44 still returns
filePatheven when thesaas-*entry is a directory anddeploy.yamlis missing. That turns an invalid registry entry into a later YAML read/parsing failure instead of skipping it cleanly.Suggested fix
func validateSaasServiceFilePath(filePath string) string { if !strings.HasPrefix(filepath.Base(filePath), "saas-") { return "" } subFilePath := filepath.Join(filePath, "deploy.yaml") if fileInfo, err := os.Stat(subFilePath); err == nil && fileInfo.Mode().IsRegular() { return subFilePath } - return filePath + if fileInfo, err := os.Stat(filePath); err == nil && fileInfo.Mode().IsRegular() { + return filePath + } + + return "" }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/saas/saas.go` around lines 34 - 45, validateSaasServiceFilePath currently can return a directory path when a "saas-*" entry is a directory and deploy.yaml is missing; change the logic so directories are never returned: in validateSaasServiceFilePath, after verifying strings.HasPrefix(filepath.Base(filePath), "saas-"), stat filePath and if it's a directory then only return filepath.Join(filePath, "deploy.yaml") when that file exists and is regular (as checked now); if deploy.yaml is missing return "" (do not return the directory); if filePath itself is a regular file return filePath as before.
🧹 Nitpick comments (1)
cmd/promote/dynatrace/dt_utils.go (1)
25-28: Make module discovery stateless.
ModulesSliceandModulesFilesMapare append-only globals, so a secondGetModulesNamescall in the same process keeps stale entries around. That can duplicate--listoutput and letValidateModuleNamesucceed with data from a previous checkout. Return fresh data from the helper instead of caching it at package scope.Also applies to: 106-123
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dt_utils.go` around lines 25 - 28, The globals ModulesSlice and ModulesFilesMap make module discovery stateful; change GetModulesNames to allocate and return fresh []string and map[string]string local variables (do not append to package-scope ModulesSlice/ModulesFilesMap), update callers (e.g., ValidateModuleName) to accept or call GetModulesNames for fresh data instead of reading globals, and remove or replace the append-only globals (and any similar package-scope caches around lines 106-123) so each call computes a new module list/map.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmd/promote/utils/app_interface_clone.go`:
- Around line 77-94: The fallback branch currently prints a diagnostic to stdout
during normal lookup attempts (the block that calls os.Getwd,
git.PlainOpenWithOptions and newAppInterfaceClone around AppInterfaceClone)
which pollutes output; remove or silence that fmt.Printf so the function does
not write to stdout during successful fallback attempts and only emits
diagnostics on final failure or via verbose/stderr logging. Change the final
diagnostic to return the error (or print to os.Stderr using
fmt.Fprintln(os.Stderr, ...)) only when no repo was found, and ensure
intermediate errors from os.Getwd, git.PlainOpenWithOptions, and
newAppInterfaceClone are not printed to stdout during normal control flow.
- Around line 110-137: Before mutating the repo, ensure the work tree is clean:
call a.workTree.Status() and if !status.IsClean() return an error refusing to
proceed; add this check at the start of CheckoutNewBranch (and optionally in
Commit) so CheckoutNewBranch and Commit do not stage unrelated edits via
a.workTree.AddGlob("."). Keep existing logic for deleting and creating branches
(repo.Reference, repo.Storer.RemoveReference, workTree.Checkout) intact but gate
them behind the cleanliness check and return a clear error mentioning the dirty
working tree and a.path.
In `@cmd/promote/utils/service.go`:
- Around line 301-324: The promote flow (function
resourceTemplatePromotion.promote) currently calls repo.ResolveHash and then
writes service.Save and SetTargetHash, which lets an invalid newHash cause files
to be rewritten before git commit/branch operations; validate that newHash
resolves to an existing ref before mutating the working copy by calling
repo.ResolveHash (or an explicit repo.ValidateRef) and returning an error if it
doesn't resolve, and only after successful validation proceed to invoke
callbacks.SetTargetHash, service.Save, callbacks.ComputeCommitMessage and
service.appInterfaceClone.Commit; apply the same pre-validation change to the
other similar promotion path that uses
callbacks.SetTargetHash/ComputeCommitMessage and service.Save (the block
referenced around the second occurrence of the promote flow).
---
Duplicate comments:
In `@cmd/promote/dynatrace/dynatrace.go`:
- Around line 93-117: The CLI flag validation for non-terraform operations
should run before cloning the repo and constructing the services registry: move
the checks that validate ops.list, ops.component, and ops.gitHash (the logic
that returns errors for "--list cannot be used with --component or --gitHash"
and "--component is required unless --list is used") above the calls to
utils.FindAppInterfaceClone and utils.NewServicesRegistry; if ops.list is true
preserve setting cmd.SilenceUsage = true and directly call
listServiceIds(servicesRegistry) only after constructing the registry when
actually needed, but prevent any repo clone/registry setup when basic flag
validation would already return an error. Ensure the condition ordering around
ops.list, ops.component and ops.gitHash is performed first so invalid flag
combinations short-circuit before invoking FindAppInterfaceClone or
NewServicesRegistry.
- Around line 128-130: The code inside the Cobra command's RunE handler (the
RunE closure in dynatrace.go that currently prints the error and calls
os.Exit(1)) should not exit the process; instead remove the fmt.Printf and
os.Exit(1) and return the error to let Cobra handle it. Update the error branch
in the RunE closure (the block that checks if err != nil after promoting the
service) to return a wrapped or raw err (e.g., fmt.Errorf("promote service: %w",
err) or simply return err) so tests and Cobra's error handling can observe the
failure.
In `@cmd/promote/saas/saas.go`:
- Around line 34-45: validateSaasServiceFilePath currently can return a
directory path when a "saas-*" entry is a directory and deploy.yaml is missing;
change the logic so directories are never returned: in
validateSaasServiceFilePath, after verifying
strings.HasPrefix(filepath.Base(filePath), "saas-"), stat filePath and if it's a
directory then only return filepath.Join(filePath, "deploy.yaml") when that file
exists and is regular (as checked now); if deploy.yaml is missing return "" (do
not return the directory); if filePath itself is a regular file return filePath
as before.
In `@cmd/promote/utils/services_registry.go`:
- Around line 35-37: Change the code that assigns serviceIdToFilePath[serviceId]
= serviceFilePath to detect duplicates and reject them instead of overwriting:
before inserting, check if serviceId already exists in serviceIdToFilePath; if
it does, return an error (or fail construction) that includes the conflicting
serviceId and both file paths (existing and new) so the caller can detect which
scanned directories produced the collision. Update the constructor/loader
function that iterates fileName/serviceFilePath to perform this check and
propagate the error back to callers.
---
Nitpick comments:
In `@cmd/promote/dynatrace/dt_utils.go`:
- Around line 25-28: The globals ModulesSlice and ModulesFilesMap make module
discovery stateful; change GetModulesNames to allocate and return fresh []string
and map[string]string local variables (do not append to package-scope
ModulesSlice/ModulesFilesMap), update callers (e.g., ValidateModuleName) to
accept or call GetModulesNames for fresh data instead of reading globals, and
remove or replace the append-only globals (and any similar package-scope caches
around lines 106-123) so each call computes a new module list/map.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9e25e1f2-5900-4e32-8376-b474d52016d8
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (32)
cmd/promote/cmd.gocmd/promote/dynatrace/dt_utils.gocmd/promote/dynatrace/dynatrace.gocmd/promote/dynatrace/utils_test.gocmd/promote/git/app_interface.gocmd/promote/git/app_interface_test.gocmd/promote/git/service_repo.gocmd/promote/git/service_repo_test.gocmd/promote/managedscripts/managed_scripts.gocmd/promote/managedscripts/managed_scripts_test.gocmd/promote/pathutil/pathutil.gocmd/promote/pathutil/pathutil_test.gocmd/promote/pko/pko.gocmd/promote/saas/saas.gocmd/promote/saas/saas_test.gocmd/promote/saas/utils.gocmd/promote/saas/utils_test.gocmd/promote/utils/app_interface_clone.gocmd/promote/utils/app_interface_clone_test.gocmd/promote/utils/git_repo.gocmd/promote/utils/service.gocmd/promote/utils/service_test.gocmd/promote/utils/services_registry.gocmd/promote/utils/services_registry_test.gocmd/promote/utils/test_tools.gocmd/promote/utils/utils_test.godocs/README.mddocs/osdctl_promote.mddocs/osdctl_promote_dynatrace.mddocs/osdctl_promote_managedscripts.mddocs/osdctl_promote_saas.mdgo.mod
💤 Files with no reviewable changes (9)
- cmd/promote/git/service_repo.go
- cmd/promote/git/service_repo_test.go
- cmd/promote/pathutil/pathutil_test.go
- cmd/promote/pathutil/pathutil.go
- cmd/promote/git/app_interface_test.go
- cmd/promote/git/app_interface.go
- cmd/promote/pko/pko.go
- cmd/promote/saas/utils.go
- cmd/promote/saas/utils_test.go
✅ Files skipped from review due to trivial changes (4)
- docs/osdctl_promote.md
- docs/osdctl_promote_dynatrace.md
- cmd/promote/utils/utils_test.go
- go.mod
🚧 Files skipped from review as they are similar to previous changes (8)
- cmd/promote/cmd.go
- cmd/promote/utils/app_interface_clone_test.go
- cmd/promote/dynatrace/utils_test.go
- cmd/promote/utils/services_registry_test.go
- cmd/promote/managedscripts/managed_scripts.go
- cmd/promote/managedscripts/managed_scripts_test.go
- cmd/promote/utils/git_repo.go
- cmd/promote/saas/saas_test.go
| func (p *resourceTemplatePromotion) promote(callbacks PromoteCallbacks, service *Service, repo *Repo, newHash string) error { | ||
| oldHash := repo.ResolveHash(p.oldHash) | ||
| fmt.Printf("Resource template (in repo) path: %s\n", p.relPath) | ||
| fmt.Printf("Resource template current hash : %v\n", oldHash) | ||
| fmt.Printf("Resource template new hash : %v\n", newHash) | ||
|
|
||
| for _, targetNode := range p.filteredTargetNodes { | ||
| err := callbacks.SetTargetHash(targetNode, newHash) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
| err := service.Save() | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| commitMessage, err := callbacks.ComputeCommitMessage(repo, p.relPath, oldHash, newHash) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| formattedCommitMessage := formatCommitMessage(commitMessage) | ||
| err = service.appInterfaceClone.Commit(formattedCommitMessage) |
There was a problem hiding this comment.
Validate the target ref before rewriting the checkout.
This flow writes service.yaml before the later git/log work can fail. Since ResolveHash does not reject lookup misses, a bad newHash can reach Line 307/Line 313, and the new saas --hotfix callback can also save app.yml before the command aborts. A failed promotion will then leave a new branch with partially updated files and no commit.
Also applies to: 448-466
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cmd/promote/utils/service.go` around lines 301 - 324, The promote flow
(function resourceTemplatePromotion.promote) currently calls repo.ResolveHash
and then writes service.Save and SetTargetHash, which lets an invalid newHash
cause files to be rewritten before git commit/branch operations; validate that
newHash resolves to an existing ref before mutating the working copy by calling
repo.ResolveHash (or an explicit repo.ValidateRef) and returning an error if it
doesn't resolve, and only after successful validation proceed to invoke
callbacks.SetTargetHash, service.Save, callbacks.ComputeCommitMessage and
service.appInterfaceClone.Commit; apply the same pre-validation change to the
other similar promotion path that uses
callbacks.SetTargetHash/ComputeCommitMessage and service.Save (the block
referenced around the second occurrence of the promote flow).
|
/retest |
MateSaary
left a comment
There was a problem hiding this comment.
Thank you for addressing the previous review!
Just mostly minor stuff I saw now, particularly one of the coderabbit suggestions (I 👎 the suggestions not worth looking at again). The comment labelled "lint" should be quick apply-able to fix the remaining CI lint issue 🙂 One other minor nit is that the help() func in promote/cmd.go is dead code, so we can remove it.
Aside from that it looks good to me at this point. Thanks!
cmd/promote/utils/test_tools.go
Outdated
| Expect(err).ShouldNot(HaveOccurred()) | ||
|
|
||
| for k := 0; k < 10; k++ { | ||
| fileName := []string{"stage.yaml", "prod1.yaml", "prod2.yaml"}[k%3] |
There was a problem hiding this comment.
lint
| fileName := []string{"stage.yaml", "prod1.yaml", "prod2.yaml"}[k%3] | |
| fileName := []string{"stage.yaml", "prod1.yaml", "prod2.yaml"}[k%3] //nolint:gosec |
There was a problem hiding this comment.
Thanks for the suggestion.
I guess the version of golangci-lint installed on my computer was a bit outdated as running make lint didn't spot this issue. Now I have updated it it is spotting other issues 😄 .
For the sake of reproducibility I wonder if we could force the use of some version of this linter.
cmd/promote/dynatrace/dynatrace.go
Outdated
|
|
||
| if err != nil { | ||
| fmt.Printf("Error while promoting service: %v\n", err) | ||
| os.Exit(1) |
There was a problem hiding this comment.
Let's get rid of this one last os.Exit that escaped 🙂
| os.Exit(1) | |
| return fmt.Errorf("error while promoting service: %v", err) |
af5846c to
2cc7523
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (5)
cmd/promote/utils/app_interface_clone.go (2)
110-137:⚠️ Potential issue | 🟠 MajorDon't stage the caller's whole checkout.
AddGlob(".")will sweep in any unrelated tracked or untracked edits already present in the reused app-interface clone. That can produce promotion commits with files this command never touched.Suggested fix
func (a *AppInterfaceClone) CheckoutNewBranch(branchName string) error { + status, err := a.workTree.Status() + if err != nil { + return fmt.Errorf("failed to inspect work tree in '%s': %w", a.path, err) + } + if !status.IsClean() { + return fmt.Errorf("refusing to reuse dirty app-interface clone '%s'; commit or stash local changes first", a.path) + } + if err := a.workTree.Checkout(&git.CheckoutOptions{Branch: plumbing.NewBranchReferenceName("master")}); err != nil { return fmt.Errorf("failed to checkout master branch in '%s': %v", a.path, err) } @@ return nil } -func (a *AppInterfaceClone) Commit(commitMessage string) error { - if err := a.workTree.AddGlob("."); err != nil { - return fmt.Errorf("failed to add files to the git index : %v", err) +func (a *AppInterfaceClone) Commit(commitMessage string, filePaths ...string) error { + for _, absPath := range filePaths { + relPath, err := filepath.Rel(a.path, absPath) + if err != nil { + return fmt.Errorf("failed to compute relative path for '%s' in '%s': %w", absPath, a.path, err) + } + if err := a.workTree.Add(relPath); err != nil { + return fmt.Errorf("failed to add '%s' to the git index: %w", relPath, err) + } } if _, err := a.workTree.Commit(commitMessage, &git.CommitOptions{}); err != nil { - return fmt.Errorf("failed to commit changes in '%s': %v", a.path, err) + return fmt.Errorf("failed to commit changes in '%s': %w", a.path, err) } return nil }Callers will need to pass the files promotion actually wrote.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/utils/app_interface_clone.go` around lines 110 - 137, The Commit method currently stages everything via a.workTree.AddGlob("."), which will include unrelated changes; change the signature of Commit to accept an explicit list of files (e.g., Commit(commitMessage string, files []string) error) and remove the AddGlob(".") call, then iterate over files and stage each one individually via a.workTree.Add (or the repo's add method) before calling a.workTree.Commit; update any callers to pass only the files the promotion actually wrote. Keep CheckoutNewBranch as-is (it just creates the branch) and ensure error messages still include a.path and the original error when staging or committing fails.
77-100:⚠️ Potential issue | 🟠 MajorKeep clone fallback silent on stdout.
The unconditional
fmt.Printfruns on the normal cwd→default fallback path. That pollutes CLI output even when clone resolution succeeds, which breaks scripting and list/data output.Suggested fix
func FindAppInterfaceClone(providedPath string) (*AppInterfaceClone, error) { if providedPath != "" { appInterfaceClone, err := newAppInterfaceClone(providedPath, nil) @@ return appInterfaceClone, nil } + var currentDirErr error { currentDirPath, err := os.Getwd() if err == nil { var currentDirRepo *git.Repository @@ } } } - fmt.Printf("Current working directory does not appear to be a suitable app-interface clone location: %v\n\n", err) + currentDirErr = err } { defaultAppInterfacePath := getDefaultAppInterfacePath() appInterfaceClone, err := newAppInterfaceClone(defaultAppInterfacePath, nil) if err != nil { - return nil, fmt.Errorf("default directory '%s' is not an app-interface clone location: %v", defaultAppInterfacePath, err) + return nil, fmt.Errorf( + "failed to resolve app-interface clone from cwd or default path: cwd=%v; default directory '%s': %w", + currentDirErr, + defaultAppInterfacePath, + err, + ) } return appInterfaceClone, nil } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/utils/app_interface_clone.go` around lines 77 - 100, The unconditional fmt.Printf that prints "Current working directory does not appear..." writes to stdout and should not pollute normal CLI output; change that call to write to stderr (use fmt.Fprintf(os.Stderr, ...)) or remove it entirely so the cwd→default fallback is silent. Update the fmt.Printf usage near the currentDirPath/newAppInterfaceClone block (the error print that uses err) to fmt.Fprintf(os.Stderr, ...) and keep the rest of the fallback logic (getDefaultAppInterfacePath and newAppInterfaceClone) unchanged.cmd/promote/dynatrace/dynatrace.go (1)
90-128:⚠️ Potential issue | 🟠 MajorValidate the non-terraform flag matrix before opening app-interface.
--liststill passes through the SaaS usage path, so it prints the component usage banner before listing, and bad flag combos can fail on clone/registry setup before returning the real CLI error.--moduleis also silently ignored here, so--component X --module Ystill promotesX.Suggested fix
} else { - ops.validateSaasFlow() + if ops.module != "" { + return errors.New("--module can only be used with --terraform") + } + if ops.list { + if ops.component != "" || ops.gitHash != "" { + return errors.New("--list cannot be used with --component or --gitHash") + } + } else if ops.component == "" { + return errors.New("--component is required unless --list is used") + } appInterfaceClone, err := utils.FindAppInterfaceClone(ops.appInterfaceProvidedPath) if err != nil { return err @@ if ops.list { - if ops.component != "" || ops.gitHash != "" { - return errors.New("--list cannot be used with --component or --gitHash") - } - cmd.SilenceUsage = true - return listServiceIds(servicesRegistry) - } else { - if ops.component == "" { - return errors.New("--component is required unless --list is used") - } - - cmd.SilenceUsage = true - - service, err := servicesRegistry.GetService(ops.component) - if err != nil { - return err - } - err = service.Promote(&utils.DefaultPromoteCallbacks{Service: service}, ops.gitHash) - - if err != nil { - return fmt.Errorf("error while promoting service: %v", err) - } } + + cmd.SilenceUsage = true + + service, err := servicesRegistry.GetService(ops.component) + if err != nil { + return err + } + err = service.Promote(&utils.DefaultPromoteCallbacks{Service: service}, ops.gitHash) + if err != nil { + return fmt.Errorf("error while promoting service: %v", err) + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dynatrace.go` around lines 90 - 128, Validate the CLI flag matrix (ops.list, ops.component, ops.gitHash, ops.module) before calling utils.FindAppInterfaceClone or utils.NewServicesRegistry: check and return errors early for invalid combos (e.g., --list cannot be used with --component or --gitHash; --component is required unless --list is used) and explicitly reject or handle --module when it would be ignored (e.g., error if ops.module != "" and ops.component != "" or otherwise document/handle module semantics) so that listServiceIds, servicesRegistry.GetService, service.Promote and related calls are only reached after flag validation.cmd/promote/utils/services_registry.go (1)
35-37:⚠️ Potential issue | 🟠 MajorReject colliding
serviceIds during registry build.This registry already merges multiple roots from
cmd/promote/saas/saas.go, so the same basename can exist in different trees. On Line 37, the later entry silently wins, which makes--serviceIdambiguous and can resolve the wrong service file.Possible fix
if serviceFilePath != "" { serviceId := strings.TrimSuffix(fileName, filepath.Ext(fileName)) + if existingPath, exists := serviceIdToFilePath[serviceId]; exists && existingPath != serviceFilePath { + return nil, fmt.Errorf("duplicate serviceId %q found in %q and %q", serviceId, existingPath, serviceFilePath) + } serviceIdToFilePath[serviceId] = serviceFilePath }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/utils/services_registry.go` around lines 35 - 37, During registry build, currently the code does serviceId := strings.TrimSuffix(fileName, filepath.Ext(fileName)) and unconditionally sets serviceIdToFilePath[serviceId] = serviceFilePath; update that logic to detect collisions: if serviceId already exists in serviceIdToFilePath and the stored path differs from serviceFilePath, return an error (or fail) instead of overwriting, including both conflicting paths and the serviceId in the error message; perform this check where serviceId and serviceFilePath are computed so --serviceId is unambiguous.cmd/promote/saas/saas.go (1)
39-44:⚠️ Potential issue | 🟡 MinorReturn only regular files from
validateSaasServiceFilePath.If
filePathis asaas-*directory anddeploy.yamlis missing, Line 44 returns the directory anyway. The registry then advertises an entry thatReadServiceFromFilecannot parse.Possible fix
subFilePath := filepath.Join(filePath, "deploy.yaml") if fileInfo, err := os.Stat(subFilePath); err == nil && fileInfo.Mode().IsRegular() { return subFilePath } - return filePath + if fileInfo, err := os.Stat(filePath); err == nil && fileInfo.Mode().IsRegular() { + return filePath + } + + return "" }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/promote/saas/saas.go` around lines 39 - 44, The function validateSaasServiceFilePath currently returns filePath even when it's a directory; change it so you only return a regular file: first check subFilePath (deploy.yaml) as you do, and if absent then os.Stat(filePath) and only return filePath if fileInfo.Mode().IsRegular(); otherwise return an empty string (or nil/error) so the registry won't advertise a directory that ReadServiceFromFile can't parse. Ensure you update the behavior in validateSaasServiceFilePath and callers that expect a non-empty file path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmd/promote/utils/test_tools.go`:
- Around line 1-14: The helper fixtures and test-only code in test_tools.go
(currently under package utils and importing . "github.com/onsi/gomega" and
repo-fixture types) must be moved into a new test-only package (e.g., package
testsupport) so they are not compiled into non-test builds; extract all helper
functions and fixture data from test_tools.go into a new file under package
testsupport, remove the test-only imports (Gomega and repo-fixture) from the
utils package, update any tests that reference those helpers to import the
testsupport package, and ensure exported helper names or test-only build tags
are used so production builds of package utils no longer pull in test
dependencies.
---
Duplicate comments:
In `@cmd/promote/dynatrace/dynatrace.go`:
- Around line 90-128: Validate the CLI flag matrix (ops.list, ops.component,
ops.gitHash, ops.module) before calling utils.FindAppInterfaceClone or
utils.NewServicesRegistry: check and return errors early for invalid combos
(e.g., --list cannot be used with --component or --gitHash; --component is
required unless --list is used) and explicitly reject or handle --module when it
would be ignored (e.g., error if ops.module != "" and ops.component != "" or
otherwise document/handle module semantics) so that listServiceIds,
servicesRegistry.GetService, service.Promote and related calls are only reached
after flag validation.
In `@cmd/promote/saas/saas.go`:
- Around line 39-44: The function validateSaasServiceFilePath currently returns
filePath even when it's a directory; change it so you only return a regular
file: first check subFilePath (deploy.yaml) as you do, and if absent then
os.Stat(filePath) and only return filePath if fileInfo.Mode().IsRegular();
otherwise return an empty string (or nil/error) so the registry won't advertise
a directory that ReadServiceFromFile can't parse. Ensure you update the behavior
in validateSaasServiceFilePath and callers that expect a non-empty file path.
In `@cmd/promote/utils/app_interface_clone.go`:
- Around line 110-137: The Commit method currently stages everything via
a.workTree.AddGlob("."), which will include unrelated changes; change the
signature of Commit to accept an explicit list of files (e.g.,
Commit(commitMessage string, files []string) error) and remove the AddGlob(".")
call, then iterate over files and stage each one individually via a.workTree.Add
(or the repo's add method) before calling a.workTree.Commit; update any callers
to pass only the files the promotion actually wrote. Keep CheckoutNewBranch
as-is (it just creates the branch) and ensure error messages still include
a.path and the original error when staging or committing fails.
- Around line 77-100: The unconditional fmt.Printf that prints "Current working
directory does not appear..." writes to stdout and should not pollute normal CLI
output; change that call to write to stderr (use fmt.Fprintf(os.Stderr, ...)) or
remove it entirely so the cwd→default fallback is silent. Update the fmt.Printf
usage near the currentDirPath/newAppInterfaceClone block (the error print that
uses err) to fmt.Fprintf(os.Stderr, ...) and keep the rest of the fallback logic
(getDefaultAppInterfacePath and newAppInterfaceClone) unchanged.
In `@cmd/promote/utils/services_registry.go`:
- Around line 35-37: During registry build, currently the code does serviceId :=
strings.TrimSuffix(fileName, filepath.Ext(fileName)) and unconditionally sets
serviceIdToFilePath[serviceId] = serviceFilePath; update that logic to detect
collisions: if serviceId already exists in serviceIdToFilePath and the stored
path differs from serviceFilePath, return an error (or fail) instead of
overwriting, including both conflicting paths and the serviceId in the error
message; perform this check where serviceId and serviceFilePath are computed so
--serviceId is unambiguous.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 91ac3f32-8f16-46f0-b938-a289940bf87f
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (32)
cmd/promote/cmd.gocmd/promote/dynatrace/dt_utils.gocmd/promote/dynatrace/dynatrace.gocmd/promote/dynatrace/utils_test.gocmd/promote/git/app_interface.gocmd/promote/git/app_interface_test.gocmd/promote/git/service_repo.gocmd/promote/git/service_repo_test.gocmd/promote/managedscripts/managed_scripts.gocmd/promote/managedscripts/managed_scripts_test.gocmd/promote/pathutil/pathutil.gocmd/promote/pathutil/pathutil_test.gocmd/promote/pko/pko.gocmd/promote/saas/saas.gocmd/promote/saas/saas_test.gocmd/promote/saas/utils.gocmd/promote/saas/utils_test.gocmd/promote/utils/app_interface_clone.gocmd/promote/utils/app_interface_clone_test.gocmd/promote/utils/git_repo.gocmd/promote/utils/service.gocmd/promote/utils/service_test.gocmd/promote/utils/services_registry.gocmd/promote/utils/services_registry_test.gocmd/promote/utils/test_tools.gocmd/promote/utils/utils_test.godocs/README.mddocs/osdctl_promote.mddocs/osdctl_promote_dynatrace.mddocs/osdctl_promote_managedscripts.mddocs/osdctl_promote_saas.mdgo.mod
💤 Files with no reviewable changes (9)
- cmd/promote/git/service_repo.go
- cmd/promote/pathutil/pathutil_test.go
- cmd/promote/git/app_interface_test.go
- cmd/promote/git/service_repo_test.go
- cmd/promote/pathutil/pathutil.go
- cmd/promote/saas/utils_test.go
- cmd/promote/saas/utils.go
- cmd/promote/pko/pko.go
- cmd/promote/git/app_interface.go
✅ Files skipped from review due to trivial changes (7)
- cmd/promote/cmd.go
- cmd/promote/utils/utils_test.go
- docs/osdctl_promote_dynatrace.md
- go.mod
- cmd/promote/utils/services_registry_test.go
- cmd/promote/saas/saas_test.go
- cmd/promote/dynatrace/dt_utils.go
🚧 Files skipped from review as they are similar to previous changes (6)
- docs/osdctl_promote.md
- cmd/promote/managedscripts/managed_scripts.go
- cmd/promote/utils/service_test.go
- cmd/promote/utils/git_repo.go
- docs/osdctl_promote_saas.md
- cmd/promote/utils/service.go
This also contains the following changes: - 'pko' promote subcommand removed as it was no longer applying to any saas file - '--osd' and '--hcp' options removed as a there is no service which could be both hcp and osd - Now exclusively using 'sigs.k8s.io/kustomize/kyaml' as both a YAML parser & marshaller as 'gopkg.in/yaml.v3' is no longer supported - Now interacting with Git repo with 'github.com/go-git/go-git/v5' lib rather than running shell commands - Code has been factorized
2cc7523 to
5ba3fc3
Compare
Nikokolas3270
left a comment
There was a problem hiding this comment.
Addressed remaining comment from AI
|
@Nikokolas3270: all tests passed! Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (4)
docs/README.md (1)
4209-4211:⚠️ Potential issue | 🟡 MinorAdd a language identifier to this fenced block.
This fence is still bare, so markdownlint MD040 will keep failing.
🛠️ Proposed fix
-``` +```text osdctl promote managedscripts [flags]</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@docs/README.mdaround lines 4209 - 4211, The fenced code block containing
"osdctl promote managedscripts [flags]" is missing a language identifier; update
that Markdown fence (the triple-backtick block that wraps that line) to include
an explicit language tag such as text or bash (e.g., changetotext) so
markdownlint MD040 stops flagging it; locate the fence that surrounds the string
"osdctl promote managedscripts [flags]" and add the identifier.</details> </blockquote></details> <details> <summary>cmd/promote/managedscripts/managed_scripts.go (1)</summary><blockquote> `63-69`: _⚠️ Potential issue_ | _🟠 Major_ **Register `managed-scripts` as the command name.** The feature is introduced as `managed-scripts`, but `Use: "managedscripts"` only exposes the unhyphenated form. `osdctl promote managed-scripts` will still be unknown. <details> <summary>🔧 Proposed fix</summary> ```diff cmd := &cobra.Command{ - Use: "managedscripts", + Use: "managed-scripts", + Aliases: []string{"managedscripts"}, Short: "Promote https://github.com/openshift/managed-scripts", @@ - osdctl promote managedscripts --gitHash <git-hash>`, + osdctl promote managed-scripts --gitHash <git-hash>`, ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@cmd/promote/managedscripts/managed_scripts.go` around lines 63 - 69, The cobra command is registered as Use: "managedscripts" but should be hyphenated; update the command definition (the Cobra command struct where Use is set—e.g., the managed scripts command in managed_scripts.go) to Use: "managed-scripts" and adjust any Example or help text that shows the command invocation to use the hyphenated form (e.g., "osdctl promote managed-scripts --gitHash <git-hash>") so the CLI recognizes osdctl promote managed-scripts. ``` </details> </blockquote></details> <details> <summary>cmd/promote/dynatrace/dynatrace.go (1)</summary><blockquote> `90-104`: _⚠️ Potential issue_ | _🟡 Minor_ **Fail fast before bootstrapping the SaaS flow.** `validateSaasFlow()` only prints text, so `--list` still emits that banner and missing-flag calls still do repo discovery before the real validation runs. <details> <summary>♻️ Suggested control-flow cleanup</summary> ```diff } else { - ops.validateSaasFlow() - - appInterfaceClone, err := utils.FindAppInterfaceClone(ops.appInterfaceProvidedPath) - if err != nil { - return err - } - - servicesRegistry, err := utils.NewServicesRegistry( - appInterfaceClone, - validateDynatraceServiceFilePath, - saasDynatraceDir, - ) - if err != nil { - return err - } - if ops.list { if ops.component != "" || ops.gitHash != "" { return errors.New("--list cannot be used with --component or --gitHash") } - cmd.SilenceUsage = true - return listServiceIds(servicesRegistry) - } else { - if ops.component == "" { - return errors.New("--component is required unless --list is used") - } + } else if ops.component == "" { + return errors.New("--component is required unless --list is used") + } - cmd.SilenceUsage = true + cmd.SilenceUsage = true + appInterfaceClone, err := utils.FindAppInterfaceClone(ops.appInterfaceProvidedPath) + if err != nil { + return err + } + servicesRegistry, err := utils.NewServicesRegistry( + appInterfaceClone, + validateDynatraceServiceFilePath, + saasDynatraceDir, + ) + if err != nil { + return err + } + if ops.list { + return listServiceIds(servicesRegistry) + } else { service, err := servicesRegistry.GetService(ops.component) ``` </details> As per coding guidelines, "Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity." Also applies to: 106-116 <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@cmd/promote/dynatrace/dynatrace.go` around lines 90 - 104, The code currently calls ops.validateSaasFlow() but continues to bootstrap the repo (utils.FindAppInterfaceClone / utils.NewServicesRegistry) even when validation should abort; change validateSaasFlow() to return an error (or a boolean+error) and update the caller to run it first and return on error before invoking utils.FindAppInterfaceClone or utils.NewServicesRegistry; propagate the new error return through ops.validateSaasFlow() calls in this file and the similar block around the 106-116 region so the command fails fast without doing repo discovery when validation fails or when flags like --list should short-circuit. ``` </details> </blockquote></details> <details> <summary>cmd/promote/saas/saas.go (1)</summary><blockquote> `34-45`: _⚠️ Potential issue_ | _🟡 Minor_ **Do not register directories as service files.** Line 44 returns `filePath` even when it is a directory without `deploy.yaml`. `utils.NewServicesRegistry` treats any non-empty callback result as a valid service path, so `--list` can surface bogus IDs and `GetService` then fails later on a non-file path. <details> <summary>Suggested fix</summary> ```diff func validateSaasServiceFilePath(filePath string) string { if !strings.HasPrefix(filepath.Base(filePath), "saas-") { return "" } subFilePath := filepath.Join(filePath, "deploy.yaml") if fileInfo, err := os.Stat(subFilePath); err == nil && fileInfo.Mode().IsRegular() { return subFilePath } - return filePath + if fileInfo, err := os.Stat(filePath); err == nil && fileInfo.Mode().IsRegular() { + return filePath + } + + return "" } ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@cmd/promote/saas/saas.go` around lines 34 - 45, validateSaasServiceFilePath currently returns the input directory path when no deploy.yaml exists, causing directories to be treated as valid service files; update validateSaasServiceFilePath to only return a non-empty string for actual files by checking that either the discovered subFilePath (deploy.yaml) is a regular file or that the original filePath itself is a regular file (use os.Stat and fileInfo.Mode().IsRegular()); if neither is a regular file, return an empty string so utils.NewServicesRegistry and GetService won't register directories as services. ``` </details> </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>Verify each finding against the current code and only fix it if needed.
Inline comments:
In@cmd/promote/dynatrace/dt_utils.go:
- Around line 38-58: getResourceTemplatesPaths currently swallows errors from
serviceRegistry.GetService and VisitElements and returns an empty string, making
parse failures indistinguishable from a legitimate no-path case; change its
signature to return (string, error), propagate and return any error from
serviceRegistry.GetService and from
service.GetResourceTemplatesSequenceNode().VisitElements instead of returning
"", and only return the joined paths and nil error on success; update any
callers to handle the (string, error) result accordingly (the same pattern
should be applied to the similar function covering lines 61-82).In
@cmd/promote/saas/saas.go:
- Around line 267-282: The CLI currently only enforces --gitHash for hotfixes
but still calls service.Promote with an empty ops.gitHash for normal promotes;
add a guard immediately before calling service.Promote to validate ops.gitHash
!= "" and return an error like "--gitHash is required" if empty so promotion
fails fast at the CLI boundary; update the block that retrieves service
(servicesRegistry.GetService / cmd.SilenceUsage) to perform this check and only
call service.Promote when ops.gitHash is present (references: ops.gitHash,
ops.isHotfix, service.Promote, promoteCallbacks).In
@cmd/promote/utils/app_interface_clone.go:
- Around line 119-122: CheckoutNewBranch currently hardcodes checking out
"master" and ignores the branchName parameter, which breaks repos using "main"
or other default branches; update CheckoutNewBranch to determine and checkout
the repository's current default branch (or accept it via branchName) instead of
plumbing.NewBranchReferenceName("master"), e.g. detect the HEAD or remote's
default ref using the repository or workTree APIs and pass that ref into
a.workTree.Checkout(&git.CheckoutOptions{Branch: ...}), then create/check out
the new branch using branchName; ensure you reference CheckoutNewBranch,
a.workTree.Checkout, plumbing.NewBranchReferenceName and the branchName
parameter when making the change.
Duplicate comments:
In@cmd/promote/dynatrace/dynatrace.go:
- Around line 90-104: The code currently calls ops.validateSaasFlow() but
continues to bootstrap the repo (utils.FindAppInterfaceClone /
utils.NewServicesRegistry) even when validation should abort; change
validateSaasFlow() to return an error (or a boolean+error) and update the caller
to run it first and return on error before invoking utils.FindAppInterfaceClone
or utils.NewServicesRegistry; propagate the new error return through
ops.validateSaasFlow() calls in this file and the similar block around the
106-116 region so the command fails fast without doing repo discovery when
validation fails or when flags like --list should short-circuit.In
@cmd/promote/managedscripts/managed_scripts.go:
- Around line 63-69: The cobra command is registered as Use: "managedscripts"
but should be hyphenated; update the command definition (the Cobra command
struct where Use is set—e.g., the managed scripts command in managed_scripts.go)
to Use: "managed-scripts" and adjust any Example or help text that shows the
command invocation to use the hyphenated form (e.g., "osdctl promote
managed-scripts --gitHash ") so the CLI recognizes osdctl promote
managed-scripts.In
@cmd/promote/saas/saas.go:
- Around line 34-45: validateSaasServiceFilePath currently returns the input
directory path when no deploy.yaml exists, causing directories to be treated as
valid service files; update validateSaasServiceFilePath to only return a
non-empty string for actual files by checking that either the discovered
subFilePath (deploy.yaml) is a regular file or that the original filePath itself
is a regular file (use os.Stat and fileInfo.Mode().IsRegular()); if neither is a
regular file, return an empty string so utils.NewServicesRegistry and GetService
won't register directories as services.In
@docs/README.md:
- Around line 4209-4211: The fenced code block containing "osdctl promote
managedscripts [flags]" is missing a language identifier; update that Markdown
fence (the triple-backtick block that wraps that line) to include an explicit
language tag such as text or bash (e.g., changetotext) so markdownlint
MD040 stops flagging it; locate the fence that surrounds the string "osdctl
promote managedscripts [flags]" and add the identifier.</details> <details> <summary>🪄 Autofix (Beta)</summary> Fix all unresolved CodeRabbit comments on this PR: - [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended) - [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes </details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: Organization UI **Review profile**: CHILL **Plan**: Pro **Run ID**: `df623ab3-2e2d-46f2-9939-b964f3388e72` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 2cc7523188364e193bd60807c8d408ef1796c09b and 5ba3fc30f55d1fd04fd3edeed23770dd84e302eb. </details> <details> <summary>⛔ Files ignored due to path filters (1)</summary> * `go.sum` is excluded by `!**/*.sum` </details> <details> <summary>📒 Files selected for processing (32)</summary> * `cmd/promote/cmd.go` * `cmd/promote/dynatrace/dt_utils.go` * `cmd/promote/dynatrace/dynatrace.go` * `cmd/promote/dynatrace/utils_test.go` * `cmd/promote/git/app_interface.go` * `cmd/promote/git/app_interface_test.go` * `cmd/promote/git/service_repo.go` * `cmd/promote/git/service_repo_test.go` * `cmd/promote/managedscripts/managed_scripts.go` * `cmd/promote/managedscripts/managed_scripts_test.go` * `cmd/promote/pathutil/pathutil.go` * `cmd/promote/pathutil/pathutil_test.go` * `cmd/promote/pko/pko.go` * `cmd/promote/saas/saas.go` * `cmd/promote/saas/saas_test.go` * `cmd/promote/saas/utils.go` * `cmd/promote/saas/utils_test.go` * `cmd/promote/utils/app_interface_clone.go` * `cmd/promote/utils/app_interface_clone_test.go` * `cmd/promote/utils/git_repo.go` * `cmd/promote/utils/service.go` * `cmd/promote/utils/service_test.go` * `cmd/promote/utils/services_registry.go` * `cmd/promote/utils/services_registry_test.go` * `cmd/promote/utils/test_tools.go` * `cmd/promote/utils/utils_test.go` * `docs/README.md` * `docs/osdctl_promote.md` * `docs/osdctl_promote_dynatrace.md` * `docs/osdctl_promote_managedscripts.md` * `docs/osdctl_promote_saas.md` * `go.mod` </details> <details> <summary>💤 Files with no reviewable changes (9)</summary> * cmd/promote/pathutil/pathutil_test.go * cmd/promote/saas/utils_test.go * cmd/promote/pathutil/pathutil.go * cmd/promote/git/service_repo.go * cmd/promote/git/app_interface_test.go * cmd/promote/git/service_repo_test.go * cmd/promote/pko/pko.go * cmd/promote/saas/utils.go * cmd/promote/git/app_interface.go </details> <details> <summary>✅ Files skipped from review due to trivial changes (3)</summary> * docs/osdctl_promote_dynatrace.md * cmd/promote/utils/service_test.go * cmd/promote/utils/utils_test.go </details> <details> <summary>🚧 Files skipped from review as they are similar to previous changes (6)</summary> * cmd/promote/cmd.go * docs/osdctl_promote.md * go.mod * cmd/promote/utils/services_registry.go * cmd/promote/utils/services_registry_test.go * cmd/promote/utils/test_tools.go </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: MateSaary, Nikokolas3270 The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
This also contains the following changes: